﻿// Jinx AJAX Framework
// Copyright (C) 2008 Blake Hegerle
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using Jinx.Applications;
using Jinx.Polling;
using Jinx.Utility.Logging;
using Core.Json;

namespace Jinx.Communications.Applications
{
    public class PollingServer : ContextualServer
    {
        public static TimeSpan DEFAULT_TIMEOUT = TimeSpan.FromMinutes(1);

        public PollingServer(ApplicationContextManager ApplicationContextManager)
            : base(ApplicationContextManager)
        {
        }

        protected override void ProcessRequest(HttpListenerContext Context,
                                               string RelativePath, ApplicationContext ApplicationContext)
        {
            TimeSpan Timeout;

            try
            {
                var Body = GetRequestBody(Context);
                var Request = Body.JsonToObject<Request>();
                Timeout = TimeSpan.Parse(Request.Timeout);
            }
            catch
            {
                Timeout = DEFAULT_TIMEOUT;
            }

            var Poller = new Poller(Context);

            ApplicationContext.PollingPlace.StartPoll(Timeout, Poller);
        }

        #region Nested type: Poller

        private class Poller : IPoller
        {
            public readonly HttpListenerContext Context;
            private readonly Stopwatch Stopwatch;

            internal Poller(HttpListenerContext Context)
            {
                this.Context = Context;
                Stopwatch = Stopwatch.StartNew();
            }

            #region IPoller Members

            public void OnTimeout()
            {
                var Response = MakeResponseObject(null);
                DoResponse(Context, 200, new ResponseObject(Response));

                Done();
            }

            public void OnUpdatesBecameAvailable(IEnumerable<Update> Updates)
            {
                var Response = MakeResponseObject(Updates);
                DoResponse(Context, 200, new ResponseObject(Response));

                Done();
            }

            #endregion

            private static object MakeResponseObject(IEnumerable<Update> Updates)
            {
                if (Updates == null)
                {
                    return new { Updates = null as object };
                }

                var UpdateResponses = from U in Updates
                                      select new UpdateResponseItem
                                             {
                                                 Path = U.Path.ToString(),
                                                 Valid = U.Valid,
                                                 Object = U.Object
                                             };

                object UpdateList = new List<UpdateResponseItem>(UpdateResponses).ToArray();

                return new { Updates = UpdateList };
            }

            private void Done()
            {
                Stopwatch.Stop();
                Log.It.Trace("Polling took {0}", Stopwatch.Elapsed);
            }

            #region Nested type: UpdateResponseItem

            private class UpdateResponseItem
            {
                public string Path { get; set; }
                public bool Valid { get; set; }
                public object Object { get; set; }
            }

            #endregion
        }

        #endregion

        #region Nested type: Request

        private class Request
        {
            public string Timeout { get; set; }
        }

        #endregion
    }
}